<?php
/**
 * 画像編集クラス
 *
 * exif、GDモジュール必須
 *
 * メモ：
 * IMAGETYPE_GIF
 * IMAGETYPE_JPEG
 * IMAGETYPE_PNG
 * IMAGETYPE_SWF
 * IMAGETYPE_PSD
 * IMAGETYPE_BMP
 * IMAGETYPE_TIFF_II (intel byte order)
 * IMAGETYPE_TIFF_MM (motorola byte order)
 * IMAGETYPE_JPC
 * IMAGETYPE_JP2
 * IMAGETYPE_JPX
 * IMAGETYPE_JB2
 * IMAGETYPE_SWC
 * IMAGETYPE_IFF
 * IMAGETYPE_WBMP
 * IMAGETYPE_XBM
 * IMAGETYPE_ICO
 *
 * @author tanaka
 * @package File
 * @subpackage ImageEditor
 */
class File_ImageEditor
{
	/*
	 * 定数 ***********************************************************************************/

	/**
	 * サポートしてないファイル
	 * @var int
	 */
	const TYPE_UNSUPPORTED = 0;

	/**
	 * 画像タイプ bmp
	 * @var int
	 */
	const TYPE_BMP = IMAGETYPE_BMP;

	/**
	 * 画像タイプ jpg
	 * @var int
	 */
	const TYPE_JPG = IMAGETYPE_JPEG;

	/**
	 * 画像タイプ gif
	 * @var int
	 */
	const TYPE_GIF = IMAGETYPE_GIF;

	/**
	 * 画像タイプ png
	 * @var int
	 */
	const TYPE_PNG = IMAGETYPE_PNG;

	/**
	 * エラーなし
	 * @var int
	 */
	const ERROR_NONE = 0;

	/**
	 * エラー アップロードされてない
	 * @var int
	 */
	const ERROR_FILE_NOT_EXIST = 1;

	/**
	 * エラー サポートしてないファイル
	 * @var int
	 */
	const ERROR_UNSUPPORTED_FILE = 2;

	/**
	 * エラー アップロード最大サイズオーバー
	 * @var int
	 */
	const ERROR_UPLOAD_MAX_SIZE_OVER = 3;

	/**
	 * エラー アップロード失敗
	 *
	 * @var int
	 */
	const ERROR_ANYTHING_UPLOAD = 4;

	/**
	 * エラー 変換処理失敗
	 *
	 * @var int
	 */
	const ERROR_EDIT_FAILD = 5;

	/**
	 * サポートしてるファイル
	 * @var array
	 */
	protected static $SUPPORT_FILE_TYPE = array(
		self::TYPE_BMP,
		self::TYPE_GIF,
		self::TYPE_JPG,
		self::TYPE_PNG,
	);

	/*
	 * 変数 ***********************************************************************************/

	/**
	 * 画像タイプ
	 *
	 * @var int
	 */
	protected $_type = self::TYPE_UNSUPPORTED;

	/**
	 * ファイル名
	 * @var string
	 */
	protected $_fileName = '';

	/**
	 * 変換前ファイルサイズ
	 * @var int
	 */
	protected $_beforSize = 0;

	/**
	 * エラー理由
	 * @var int
	 */
	protected $_error = self::ERROR_FILE_NOT_EXIST;

	/**
	 * テンポラリにあるファイル名
	 *
	 * @var string
	 */
	protected $_targetFile = '';

	/**
	 * 編集完了してるか
	 * @var bool
	 */
	protected $_isFinishEdit = false;

	/**
	 * ファイルの編集後バイナリ
	 * @var string
	 */
	protected $_binary = '';

	/**
	 * 変換後ファイルサイズ
	 * @var int
	 */
	protected $_afterSize = 0;

	/*
	 * 関数 ***********************************************************************************/

	/**
	 * コンストラクタ
	 *
	 * @param string $targetFile
	 * @param string $fileName
	 * @param int $size
	 * @return
	 */
	public function __construct($targetFile, $fileName, $size = 0)
	{
		$this->_targetFile = $targetFile;
		$this->_fileName = $fileName;
		$this->_beforSize = $size;

		if (!file_exists($this->_targetFile) || !is_readable($this->_targetFile)) return;

		if ($size = 0) {
			$this->_beforSize = filesize($this->_targetFile);
		}

		$type = exif_imagetype($this->_targetFile);

		if (!in_array($type, self::$SUPPORT_FILE_TYPE)) {
			$this->_error = self::ERROR_UNSUPPORTED_FILE;
			return;
		}

		$this->_type = $type;
		$this->_error = self::ERROR_NONE;
	}

	/**
	 * エラー理由を取得
	 *
	 * @return int
	 */
	public function getError()
	{
		return $this->_error;
	}

	/**
	 * エラーかどうか返します。
	 *
	 * @return
	 */
	public function isValid()
	{
		return self::ERROR_NONE == $this->getError();
	}

	/**
	 * ファイル名称を取得します。
	 *
	 * @return string
	 */
	public function getFileName()
	{
		return $this->_fileName;
	}

	/**
	 * ファイルタイプを取得します。
	 *
	 * @return int
	 */
	public function getFileType()
	{
		if($this->_type == self::TYPE_BMP){
			return self::TYPE_JPG;
		}
		
		return $this->_type;
	}

	/**
	 * 変換したファイルの取得
	 *
	 * @return string
	 */
	public function getEditData()
	{
		if ($this->_error != self::ERROR_NONE) {
			return '';
		}

		if (!$this->_isFinishEdit) {
			$this->_isFinishEdit = true;
			$this->_binary = $this->_getEditImage($this->_targetFile, C_PRODUCT_IMAGE_LENGTH);
			$this->_afterSize = strlen($this->_binary);
		}

		return $this->_binary;
	}

	/**
	 * 縦横比固定で横の長さのみ指定でファイルを編集します。
	 *
	 * @param string $fileName ファイル名
	 * @param int $length 長辺の長さ
	 * @return string
	 */
	protected function _getEditImage($fileName, $length)
	{
		//元画像のサイズを取得
		list($oldWidth, $oldHeight) = getimagesize($fileName);

		//縦長か横長かをチェック
		if ($oldWidth > $oldHeight) {
			//横長の場合
			$newHeight = ($oldHeight / $oldWidth) * $length;
			$newWidth = $length;
		}
		else {
			//縦長の場合
			$newWidth = ($oldWidth / $oldHeight) * $length;
			$newHeight = $length;
		}

		//元画像を読み込み
		switch ($this->_type) {
			case self::TYPE_BMP:
				$oldResource = $this->_imageCreateFromBmp($fileName);
				break;

			case self::TYPE_GIF:
				$oldResource = imagecreatefromgif($fileName);
				break;

			case self::TYPE_JPG:
				$oldResource = imagecreatefromjpeg($fileName);
				break;

			case self::TYPE_PNG:
				$oldResource = imagecreatefrompng($fileName);
				break;

			default:
				$this->_error = self::ERROR_EDIT_FAILD;
				return '';
		}

		if ($oldResource === false) {
			$this->_error = self::ERROR_EDIT_FAILD;
			return '';
		}

		//リサイズ用の画像を作成
		$newResource = imagecreatetruecolor($newWidth, $newHeight); //元画像からリサイズしてコピー
		imagecopyresampled($newResource, $oldResource, 0, 0, 0, 0, $newWidth, $newHeight, $oldWidth, $oldHeight);
		imagedestroy($oldResource);

		// 文字列化
		ob_start();
		switch ($this->_type) {
			case self::TYPE_BMP:
				// jpeg化
				$this->_type = self::TYPE_JPG;
				imagejpeg($newResource, null, 100);
				break;

			case self::TYPE_GIF:
				imagegif($newResource);
				break;

			case self::TYPE_JPG:
				imagejpeg($newResource, null, 100);
				break;

			case self::TYPE_PNG:
				imagepng($newResource, null, 0);
				break;

			default:
				$this->_error = self::ERROR_EDIT_FAILD;
				ob_end_clean();
				return '';
		}

		$image = ob_get_contents();
		ob_end_clean();

		imagedestroy($newResource);

		return $image;
	}

	/**
	 * BMP対応版イメージクリエイト
	 * 
	 * BMPの場合リサイズ処理がうまくいかない
	 * 
	 * @param string $filename
	 * @return resource
	 */
	protected function _imageCreateFromBmp($filename)
	{
		//Ouverture du fichier en mode binaire
		if (!$f1 = fopen($filename, "rb")) return FALSE;

		//1 : Chargement FICHIER
		$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
		if ($FILE['file_type'] != 19778) return FALSE;

		//2 : Chargement BMP
		$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel' .
			'/Vcompression/Vsize_bitmap/Vhoriz_resolution' .
			'/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
		$BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
		if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
		$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel'] / 8;
		$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
		$BMP['decal'] = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
		$BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
		$BMP['decal'] = 4 - (4 * $BMP['decal']);
		if ($BMP['decal'] == 4) $BMP['decal'] = 0;

		//3 : Chargement des couleurs de la palette
		$PALETTE = array();
		if ($BMP['colors'] < 16777216) {
			$PALETTE = unpack('V' . $BMP['colors'], fread($f1, $BMP['colors'] * 4));
		}

		//4 : 
		$IMG = fread($f1, $BMP['size_bitmap']);
		$VIDE = chr(0);

		$res = imagecreatetruecolor($BMP['width'], $BMP['height']);
		$P = 0;
		$Y = $BMP['height'] - 1;
		while ($Y >= 0) {
			$X = 0;
			while ($X < $BMP['width']) {
				if ($BMP['bits_per_pixel'] == 24) $COLOR = unpack("V", substr($IMG, $P, 3) . $VIDE);
				elseif ($BMP['bits_per_pixel'] == 16) {
					$COLOR = unpack("n", substr($IMG, $P, 2));
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				elseif ($BMP['bits_per_pixel'] == 8) {
					$COLOR = unpack("n", $VIDE . substr($IMG, $P, 1));
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				elseif ($BMP['bits_per_pixel'] == 4) {
					$COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
					if (($P * 2) % 2 == 0) $COLOR[1] = ($COLOR[1] >> 4);
					else $COLOR[1] = ($COLOR[1] & 0x0F);
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				elseif ($BMP['bits_per_pixel'] == 1) {
					$COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
					if (($P * 8) % 8 == 0) $COLOR[1] = $COLOR[1] >> 7;
					elseif (($P * 8) % 8 == 1) $COLOR[1] = ($COLOR[1] & 0x40) >> 6;
					elseif (($P * 8) % 8 == 2) $COLOR[1] = ($COLOR[1] & 0x20) >> 5;
					elseif (($P * 8) % 8 == 3) $COLOR[1] = ($COLOR[1] & 0x10) >> 4;
					elseif (($P * 8) % 8 == 4) $COLOR[1] = ($COLOR[1] & 0x8) >> 3;
					elseif (($P * 8) % 8 == 5) $COLOR[1] = ($COLOR[1] & 0x4) >> 2;
					elseif (($P * 8) % 8 == 6) $COLOR[1] = ($COLOR[1] & 0x2) >> 1;
					elseif (($P * 8) % 8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				else return FALSE;
				imagesetpixel($res, $X, $Y, $COLOR[1]);
				$X++;
				$P += $BMP['bytes_per_pixel'];
			}
			$Y--;
			$P += $BMP['decal'];
		}

		fclose($f1);

		return $res;
	}
}
